<!-- 代码已包含 CSS：使用 TailwindCSS , 安装 TailwindCSS 后方可看到布局样式效果 -->
<template>
<div class="min-h-screen bg-gray-50 p-8">
<div class="mx-auto max-w-7xl">
<!-- 标题区域 -->
<div class="mb-8">
<h1 class="text-2xl font-bold text-gray-900">管理员配置</h1>
<p class="mt-2 text-gray-600">配置系统管理员、企业管理员及项目部管理员</p>
</div>
<!-- 管理员列表 -->
<div class="mb-6 rounded-lg bg-white p-6 shadow-sm">
<div class="mb-4 flex items-center justify-between">
<div class="flex items-center space-x-4">
<el-input
v-model="searchKeyword"
placeholder="搜索管理员"
class="w-64"
:prefix-icon="Search"
/>
<el-select v-model="filterType" placeholder="管理员类型" class="w-40">
<el-option label="全部类型" value="" />
<el-option
v-for="type in adminTypes"
:key="type.value"
:label="type.label"
:value="type.value"
/>
</el-select>
</div>
<el-button
type="primary"
class="!rounded-button whitespace-nowrap"
@click="showAddDialog = true"
>
<el-icon class="mr-1"><Plus /></el-icon>新增管理员
</el-button>
</div>
<el-table :data="filteredAdminList" stripe>
<el-table-column prop="account" label="管理员账号" width="150" />
<el-table-column prop="name" label="管理员名称" width="150" />
<el-table-column prop="enterprise" label="企业名称" width="180" />
<el-table-column prop="type" label="管理员类型" width="150">
<template #default="{ row }">
<el-tag
:type="getAdminTypeTag(row.type)"
class="whitespace-nowrap"
>
{{ getAdminTypeLabel(row.type) }}
</el-tag>
</template>
</el-table-column>
<el-table-column prop="departments" label="管理项目部" show-overflow-tooltip>
<template #default="{ row }">
<div class="flex flex-wrap gap-1">
<el-tag
v-for="dept in row.departments"
:key="dept"
type="info"
class="whitespace-nowrap"
>
{{ dept }}
</el-tag>
</div>
</template>
</el-table-column>
<el-table-column prop="createTime" label="创建时间" width="180" />
<el-table-column label="操作" width="200">
<template #default="{ row }">
<el-button
type="primary"
link
class="!rounded-button whitespace-nowrap"
@click="handleEdit(row)"
>
编辑
</el-button>
<el-button
type="danger"
link
class="!rounded-button whitespace-nowrap"
@click="handleDelete(row)"
>
删除
</el-button>
</template>
</el-table-column>
</el-table>
<div class="mt-4 flex justify-end">
<el-pagination
v-model:current-page="currentPage"
v-model:page-size="pageSize"
:total="total"
:page-sizes="[10, 20, 50, 100]"
layout="total, sizes, prev, pager, next"
/>
</div>
</div>
<!-- 新增/编辑对话框 -->
<el-dialog
v-model="showAddDialog"
:title="isEdit ? '编辑管理员' : '新增管理员'"
width="600px"
>
<el-form
ref="formRef"
:model="formData"
:rules="rules"
label-width="120px"
>
<el-form-item label="管理员账号" prop="account">
<el-input v-model="formData.account" placeholder="请输入管理员账号" />
<div class="mt-1 text-xs text-gray-500">账号只能是字母+数字的组合，长度 4-20 位</div>
</el-form-item>
<el-form-item label="管理员名称" prop="name">
<el-input v-model="formData.name" placeholder="请输入管理员名称" />
</el-form-item>
<el-form-item label="企业名称" prop="enterprise">
<el-input v-model="formData.enterprise" placeholder="请输入企业名称" />
</el-form-item>
<el-form-item label="管理员类型" prop="type">
<el-select v-model="formData.type" class="w-full">
<el-option
v-for="type in adminTypes"
:key="type.value"
:label="type.label"
:value="type.value"
/>
</el-select>
</el-form-item>
<el-form-item
label="登录密码"
prop="password"
:rules="isEdit ? [] : rules.password"
>
<el-input
v-model="formData.password"
type="password"
placeholder="请输入密码"
show-password
/>
</el-form-item>
<el-form-item
label="确认密码"
prop="confirmPassword"
:rules="isEdit ? [] : rules.confirmPassword"
>
<el-input
v-model="formData.confirmPassword"
type="password"
placeholder="请再次输入密码"
show-password
/>
</el-form-item>
<el-form-item label="管理项目部" prop="departments">
<el-tree-select
v-model="formData.departments"
:data="departmentOptions"
multiple
show-checkbox
check-strictly
:render-after-expand="false"
placeholder="请选择管理项目部"
/>
</el-form-item>
</el-form>
<template #footer>
<el-button @click="showAddDialog = false">取消</el-button>
<el-button
type="primary"
class="!rounded-button whitespace-nowrap"
@click="handleSubmit"
>
确认
</el-button>
</template>
</el-dialog>
</div>
</div>
</template>
<script lang="ts" setup>
import { ref, computed } from 'vue';
import type { FormInstance, FormRules } from 'element-plus';
import { Search, Plus } from '@element-plus/icons-vue';
import { ElMessage, ElMessageBox } from 'element-plus';
// 管理员类型
const adminTypes = [
{ label: '系统管理员', value: 'system' },
{ label: '企业管理员', value: 'enterprise' },
{ label: '项目部管理员', value: 'project' },
];
// 表单相关
const formRef = ref<FormInstance>();
const showAddDialog = ref(false);
const isEdit = ref(false);
const formData = ref({
account: '',
name: '',
enterprise: '',
type: '',
password: '',
confirmPassword: '',
departments: [] as string[],
});
// 表单校验规则
const validatePass = (rule: any, value: string, callback: any) => {
if (value === '') {
callback(new Error('请输入密码'));
} else {
if (formData.value.confirmPassword !== '') {
formRef.value?.validateField('confirmPassword');
}
callback();
}
};
const validatePass2 = (rule: any, value: string, callback: any) => {
if (value === '') {
callback(new Error('请再次输入密码'));
} else if (value !== formData.value.password) {
callback(new Error('两次输入密码不一致!'));
} else {
callback();
}
};
const rules = {
account: [
{ required: true, message: '请输入管理员账号', trigger: 'blur' },
{ pattern: /^[a-zA-Z0-9]{4,20}$/, message: '账号只能是字母+数字的组合，长度 4-20 位', trigger: 'blur' }
],
name: [{ required: true, message: '请输入管理员名称', trigger: 'blur' }],
enterprise: [{ required: true, message: '请输入企业名称', trigger: 'blur' }],
type: [{ required: true, message: '请选择管理员类型', trigger: 'change' }],
password: [{ validator: validatePass, trigger: 'blur' }],
confirmPassword: [{ validator: validatePass2, trigger: 'blur' }],
departments: [
{ required: true, message: '请选择管理项目部', trigger: 'change' },
],
};
// 列表数据
const searchKeyword = ref('');
const filterType = ref('');
const currentPage = ref(1);
const pageSize = ref(10);
const total = ref(100);
const adminList = ref([
{
id: 1,
account: 'zhangjg',
name: '张建国',
enterprise: '远大科技集团有限公司',
type: 'system',
departments: ['技术研发中心', '产品设计部'],
createTime: '2024-01-15 14:30:00',
},
{
id: 2,
account: 'lizy',
name: '李志远',
enterprise: '智慧建筑科技有限公司',
type: 'enterprise',
departments: ['人力资源部', '财务部'],
createTime: '2024-01-14 09:15:00',
},
{
id: 3,
account: 'wanghm',
name: '王慧敏',
enterprise: '创新工程技术有限公司',
type: 'project',
departments: ['工程一部'],
createTime: '2024-01-13 16:45:00',
},
]);
const filteredAdminList = computed(() => {
return adminList.value.filter((item) => {
const matchKeyword = item.name
.toLowerCase()
.includes(searchKeyword.value.toLowerCase());
const matchType = !filterType.value || item.type === filterType.value;
return matchKeyword && matchType;
});
});
// 部门数据
const departmentOptions = [
{
value: '1',
label: '总公司',
children: [
{
value: '1-1',
label: '技术研发中心',
},
{
value: '1-2',
label: '产品设计部',
},
],
},
{
value: '2',
label: '分公司',
children: [
{
value: '2-1',
label: '工程一部',
},
{
value: '2-2',
label: '工程二部',
},
],
},
];
// 获取管理员类型标签
const getAdminTypeTag = (type: string) => {
const map: Record<string, string> = {
system: '',
enterprise: 'success',
project: 'warning',
};
return map[type];
};
const getAdminTypeLabel = (type: string) => {
const found = adminTypes.find((item) => item.value === type);
return found ? found.label : '';
};
// 编辑管理员
const handleEdit = (row: any) => {
isEdit.value = true;
formData.value = {
...row,
password: '',
confirmPassword: '',
};
showAddDialog.value = true;
};
// 删除管理员
const handleDelete = (row: any) => {
ElMessageBox.confirm('确认删除该管理员吗？', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
})
.then(() => {
ElMessage({
type: 'success',
message: '删除成功',
});
})
.catch(() => {});
};
// 提交表单
const handleSubmit = async () => {
if (!formRef.value) return;
await formRef.value.validate((valid) => {
if (valid) {
ElMessage({
type: 'success',
message: isEdit.value ? '修改成功' : '添加成功',
});
showAddDialog.value = false;
}
});
};
</script>
<style scoped>
.el-input :deep(.el-input__wrapper) {
box-shadow: 0 0 0 1px #e5e7eb inset;
}
.el-input :deep(.el-input__wrapper):hover {
box-shadow: 0 0 0 1px #d1d5db inset;
}
.el-select :deep(.el-input__wrapper) {
box-shadow: 0 0 0 1px #e5e7eb inset;
}
.el-select :deep(.el-input__wrapper):hover {
box-shadow: 0 0 0 1px #d1d5db inset;
}
.el-tree-select :deep(.el-select__wrapper) {
box-shadow: 0 0 0 1px #e5e7eb inset;
}
.el-input :deep(.el-input__inner) {
font-size: 14px;
}
.el-input :deep(.el-input__prefix) {
display: flex;
align-items: center;
}
</style>
